package entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;

import entity.Iteracao.SituacaoIteracao;
import exception.AlmiranteException;

@Entity
@Table(name="fase", schema="almirante")
public class Fase {
	
	@EmbeddedId
	private FasePK id;
	
	@MapsId("idProjeto")
	@ManyToOne
	@JoinColumn(name="id_projeto", referencedColumnName="id_projeto")
	private Projeto projeto;
	
	@OneToMany(mappedBy="fase", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
	@OrderBy("numero")
	private List<Iteracao> iteracoes = new ArrayList<Iteracao>();
	
	public enum FaseRUP {
		INICIACAO("Iniciação"), ELABORACAO("Elaboração"), CONSTRUCAO("Construção"), TRANSICAO("Transição");
		
		public final String nome;
		private FaseRUP(String nome) {
			this.nome = nome;
		}
		public String getNome() { return nome; }
		public int getIndice() {
			return this.ordinal();
		}
		public static FaseRUP get(int indice) {
			return values()[indice];
		}
	}
	
	/** Para uso do hibernate. */
	public Fase() {}
	
	public Fase(Projeto projeto, FaseRUP faseRup) {
		this.id = new FasePK(faseRup, projeto.getId());
		this.projeto = projeto;
	}
	
	public Date getInicio() {
		if (possuiIteracao()) {
			return primeiraIteracao().getInicio();
		}
		return null;
	}

	public Date getFim() {
		if (possuiIteracao())
			return ultimaIteracao().getFim();
		return null;
	}
	
	public int getDuracao() {
		if (possuiIteracao())
			return new Periodo(primeiraIteracao().getInicio(), ultimaIteracao().getFim()).intervaloEmDias();
		
		return 0;
	}
	
	public boolean possuiIteracao() {
		return !iteracoes.isEmpty();
	}
	
	private Iteracao primeiraIteracao() {
		if (possuiIteracao())
			return iteracoes.get(0);
		return null;
	}
	
	private Iteracao ultimaIteracao() {
		if (possuiIteracao())
			return iteracoes.get(iteracoes.size() - 1);
		return null;
	}
	
	void adicionarIteracao(Iteracao iteracao) {
		iteracoes.add(iteracao);		
		iteracao.setFase(this);
		iteracao.setNumero(iteracoes.size());
	}
	
	void removerIteracao(Integer numero) {
		if (numero <= 0)
			throw new AlmiranteException("Número da iteração inválido: " + 0);
		
		if (iteracoes.size() < numero)
			throw new AlmiranteException("Iteração com número " + numero + " não existente.");
		
		Iteracao iteracao = getIteracao(numero);
		if (iteracao.getSituacao().equals(SituacaoIteracao.EM_ANDAMENTO) ||
				iteracao.getSituacao().equals(SituacaoIteracao.PLANEJADA) ||
				iteracao.getSituacao().equals(SituacaoIteracao.CONCLUIDA))
			throw new AlmiranteException("Iteração de " + getFaseRup().getNome() + " #" + iteracao.getNumero() + 
					" não pode ser removida, pois está " + iteracao.getSituacao().getDescricao());
		
		iteracoes.remove(iteracao);
		
		if (possuiIteracao()) {
			int numeroNovo = 0;
			for (Iteracao i : iteracoes) {
				i.setNumero(++numeroNovo);
			}
		}
	}
	
	public Iteracao getIteracao(Integer numero) {
		return iteracoes.get(numero - 1);
	}
	
	public Integer getHorasEstimadas() {
		int horas = 0;
		if (possuiIteracao()) {
			for (Iteracao iteracao : iteracoes) {
				if (iteracao.getHorasEstimadas() != null)
					horas += iteracao.getHorasEstimadas();
			}
		}
		return horas;
	}
	
	public Integer getHorasRealizadas() {
		int horas = 0;
		if (possuiIteracao()) {
			for (Iteracao iteracao : iteracoes) {
				horas += iteracao.getHorasRealizadas();
			}
		}
		return horas;
	}
	
	public String getNome() {
		return this.id.getFaseRup().nome;
	}
	public FaseRUP getFaseRup() {
		return this.id.getFaseRup();
	}
	public List<Iteracao> getIteracoes() {
		return Collections.unmodifiableList(iteracoes);
	}
	public FasePK getId() {
		return this.id;
	}
	public Integer getIndice() {
		return this.id.getFaseRup().ordinal();
	}
	public Projeto getProjeto() {
		return this.projeto;
	}
	
}
